package com.zjmzxfzhl.modules.flowable.service.impl;

import com.zjmzxfzhl.common.core.Result;
import com.zjmzxfzhl.common.core.util.ObjectUtils;
import com.zjmzxfzhl.modules.flowable.mapper.FlowableCommonMapper;
import com.zjmzxfzhl.modules.flowable.mapper.WorkflowMapper;
import com.zjmzxfzhl.modules.flowable.service.FlowableTaskService;
import com.zjmzxfzhl.modules.flowable.service.WorkflowService;
import com.zjmzxfzhl.modules.flowable.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.*;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * @author 庄金明
 * @date 2020年3月23日
 */
@Service
@Slf4j
public class WorkflowServiceImpl implements WorkflowService {
    @Autowired
    protected WorkflowMapper workflowMapper;

    @Autowired
    protected FlowableTaskService flowableTaskService;

    @Autowired
    protected RepositoryService repositoryService;

    @Override
    public WorkflowVo findTaskByProcessInstanceId(WorkflowVo vo) {
        return workflowMapper.findTaskByProcessInstanceId(vo);
    }

    @Override
    public Result complete(TaskRequest taskRequest) {
        WorkflowVo vo = new WorkflowVo();
        if (StringUtils.isEmpty(taskRequest.getProcessInstanceId())) {
            return Result.error("processInstanceId cannot be null");
        }
        vo.setProcessInstanceId(taskRequest.getProcessInstanceId());
        vo.setTaskName(taskRequest.getTaskName());
        vo.setTaskDefinitionKey(taskRequest.getTaskDefinitionKey());
        WorkflowVo resultVo = findTaskByProcessInstanceId(vo);
        if (ObjectUtils.isEmpty(resultVo)) {
            return Result.error("cannot find valid task");
        }
        taskRequest.setTaskId(resultVo.getTaskId());
        flowableTaskService.completeTask(taskRequest);
        return Result.ok();
    }

    @Override
    public List<WorkflowVo> findProcessUserTaskByPdId(String processDefinitionId) {
        List<WorkflowVo> processTasks = new ArrayList<WorkflowVo>();
        if(StringUtils.isEmpty(processDefinitionId)){
            return processTasks;
        }
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        if(pd == null){
            return processTasks;
        }else{
            return findProcessUserTaskByPd(pd);
        }
    }

    private List<WorkflowVo> findProcessUserTaskByPd(ProcessDefinition pd){
        List<WorkflowVo> processTasks = new ArrayList<WorkflowVo>();
        WorkflowVo processTask = null;
        String resName = pd.getResourceName();
        InputStream resouceStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), resName);
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in;
        XMLStreamReader xtr;
        try {
            in = new InputStreamReader(resouceStream, "UTF-8");
            xtr = xif.createXMLStreamReader(in);
            BpmnModel model = new BpmnXMLConverter().convertToBpmnModel(xtr);
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for(FlowElement e : flowElements) {
                String clazz = e.getClass().toString();
                if(clazz.endsWith("UserTask")){
                    processTask = new WorkflowVo();
                    processTask.setTaskKey(e.getId());
                    processTask.setTaskName(e.getName());
                    UserTask userTask = (UserTask) e;
                    processTask.setTaskAssignee(userTask.getAssignee());
                    processTasks.add(processTask);
                }
            }
        } catch (XMLStreamException e) {
            log.error("findProcessUserTaskByPd1", e);
        } catch (UnsupportedEncodingException e) {
            log.error("findProcessUserTaskByPd2", e);
        }
        return processTasks;
    }
}
